use std::string::String;
use super::*;
use log;
use serde_json::json;
use std::ops::Add;
use std::path::Path;
use base64::engine::general_purpose;
use serde_json::Value;

const ITRUSTEE_REF_VALUE_DIR: &str =
    "/etc/attestation/attestation-service/reference-itrustee/";

#[derive(Debug, Default)]
pub struct ItrusteeVerifier {}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct buffer_data {
    pub size: ::std::os::raw::c_uint,
    pub buf: *mut ::std::os::raw::c_uchar,
}

#[link(name = "teeverifier")]
unsafe extern "C" {
    pub fn tee_verify_report(
        data_buf: *mut buffer_data,
        nonce: *mut buffer_data,
        type_: ::std::os::raw::c_int,
        filename: *mut ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}


pub type TeeClaim = serde_json::Value;

impl ItrusteeVerifier {
    pub async fn evaluate(&self, user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim, Box<dyn std::error::Error>> {
        evalute_wrapper(user_data, evidence)
    }
}
const MAX_CHALLENGE_LEN: usize = 64;
fn evalute_wrapper(user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim, Box<dyn std::error::Error>> {
    let challenge = general_purpose::STANDARD.decode(user_data)?;
    let len = challenge.len();
    if len <= 0 || len > MAX_CHALLENGE_LEN {
        log::error!(
            "challenge len is error, expecting 0 < len <= {}, got {}",
            MAX_CHALLENGE_LEN,
            len
        );
    }
    let mut in_data = challenge.to_vec();
    let mut in_evidence = evidence.to_vec();
    let mut data_buf: buffer_data = buffer_data {
        size: in_evidence.len() as ::std::os::raw::c_uint,
        buf: in_evidence.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
    };
    let mut nonce = buffer_data {
        size: in_data.len() as ::std::os::raw::c_uint,
        buf: in_data.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
    };
    // parse uuid from evidence to find it's basevalue file that store in ITRUSTEE_REF_VALUE_DIR
    let evidence_json:Value = serde_json::from_slice(evidence)?;
    println!("{}", serde_json::to_string_pretty(&evidence_json).unwrap());
    let mut uuid = String::new();
    if let Some(v)= evidence_json.get("payload")
        .and_then(|v|v.get("uuid"))
        .and_then(|v|v.as_str()) {
        uuid = v.to_string();
    }
    else {
        log::error!("parse uuid from evidence failed");
    }
    let policy: std::os::raw::c_int = 1; // 1: verify ta_imag; 2: verfiy ta_mem; 3: verify ta_img and ta_mem hash;
    let ref_value_file = ITRUSTEE_REF_VALUE_DIR.to_string() + "itrustee_" + &uuid;
    if !Path::new(&ref_value_file).exists() {
        log::error!(
            "itrustee verify report {} not exists",
            ref_value_file
        );
    }
    let ref_file = String::from(ref_value_file);
    let mut file = ref_file.add("\0");
    let basevalue = file.as_mut_ptr() as *mut ::std::os::raw::c_char;
    unsafe {
        let ret = tee_verify_report(&mut data_buf, &mut nonce, policy, basevalue);
        if ret != 0 {
            log::error!("itrustee verify report failed ret:{}", ret);
        }
    }
    let js_evidence: serde_json::Value = serde_json::from_slice(evidence)?;
    let payload = json!({
        "itrustee.nonce": js_evidence["payload"]["nonce"].clone(),
        "itrustee.hash_alg": js_evidence["payload"]["hash_alg"].clone(),
        "itrustee.key": js_evidence["payload"]["key"].clone(),
        "itrustee.ta_img": js_evidence["payload"]["ta_img"].clone(),
        "itrustee.ta_mem": js_evidence["payload"]["ta_mem"].clone(),
        "itrustee.uuid": js_evidence["payload"]["uuid"].clone(),
        "itrustee.version": js_evidence["payload"]["version"].clone(),
    });
    let claim = json!({
        "tee": "itrustee",
        "payload" : payload,
    });
    Ok(claim as TeeClaim)
}
